/*
实现一个命令行解释器程序，能够解释以下命令：
chown <user> <file>：改变文件的拥有者 别名：own
chmod <mode> <file>：改变文件的权限 别名：mod
kill <pid>：杀死进程 别名：slay
java <file> --inps "参数"：执行jar文件 别名：java
vi <file>：编辑文件 别名：edit
运维专区敏感指令：如下
.*reboot.*：重启,.*init 0.*：关机,.*shutdown.*：关机,.*halt.*：关机
.*rm -rf.*：删除,.*chmod.*：修改,.*chown.*：修改,.*kill.*：杀死,.*java.*：执行
.*mysqldump.*：备份,.*expdp.*:导入导出, .*mysql -u -p.*:登录,
.*useradd.*：添加用户,.*passwd.*：修改密码,.*groupadd.*：添加组,.*groupmod.*：修改组,.*usermod.*：修改用户
.*start.*：启动,.*stop.*：停止,.*restart.*：重启,./:执行脚本,.*bash.*:执行脚本
*/
use anyhow::{anyhow, Result};
use std::path::PathBuf;
use std::process::Command;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
pub enum Action {
    /// 改变文件的拥有者
    /// 例如：
    ///  rtcmd own -f [file|directory] -u username.;
    /// rtcmd own -f [file|directory] -u username:usergroup;
    #[structopt(name = "own")]
    Own {
        #[structopt(short, long)]
        user: String,
        #[structopt(short, long)]
        file: PathBuf,
    },
    /// 改变文件的权限
    /// 例如：
    ///  rtcmd mod -f [file|directory] -m +x;
    /// rtcmd mod -f [file|directory] -m 775;
    #[structopt(name = "mod")]
    Mod {
        #[structopt(short, long)]
        mode: String,
        #[structopt(short, long)]
        file: PathBuf,
    },
    /// 杀死进程;
    /// 例如：
    /// rtcmd slay -p pid;
    /// rtcmd slay -p "pid1 pid2 pid3";
    #[structopt(name = "slay")]
    Slay {
        #[structopt(short, long)]
        pid: String,
    },
    /// 执行jar文件
    /// 例如：
    /// rtcmd java -f filename.jar;
    /// rtcmd java -f filename.jar -i "Xms128m Xmx256m Xmn128m";
    #[structopt(name = "java")]
    Jar {
        #[structopt(short, long)]
        file: PathBuf,
        #[structopt(short, long, default_value = "")]
        inps: String,
    },
    /// 执行vi文件编辑工作
    /// 例如：
    /// rtcmd edit -f filename;
    #[structopt(name = "edit")]
    Edit {
        #[structopt(short, long)]
        file: PathBuf,
    },
    /// 帮助
    /// 例如：
    /// rtcmd help; rtcmd -h;
    #[structopt(name = "help")]
    Help,
}

#[derive(Debug, StructOpt)]
#[structopt(
    version = "v0.1.0",
    name = "rtcmd",
    about = "这是个在特别专区使用的命令行解释器"
)]
pub struct Opts {
    #[structopt(subcommand)]
    pub action: Action,
}
impl Opts {
    pub fn run(&self) -> Result<(), anyhow::Error> {
        match &self.action {
            Action::Own { user, file } => {
                let output = Command::new("chown")
                    .arg(user)
                    .arg(file)
                    .spawn()?
                    .wait_with_output()?;
                if !output.status.success() {
                    return Err(anyhow!("chown command failed to start"));
                }
            }
            Action::Mod { mode, file } => {
                let output = Command::new("chmod")
                    .arg(mode)
                    .arg(file)
                    .spawn()?
                    .wait_with_output()?;
                if !output.status.success() {
                    return Err(anyhow!("chmod command failed to start"));
                }
            }
            Action::Slay { pid } => {
                let output = Command::new("kill")
                    .arg("-9".to_string())
                    .args(
                        pid.to_string()
                            .split_whitespace()
                            .map(|s| s.to_string())
                            .collect::<Vec<String>>(),
                    )
                    .spawn()?
                    .wait_with_output()?;
                if !output.status.success() {
                    return Err(anyhow!("java command failed to start"));
                }
            }
            Action::Jar { file, inps } => {
                let inps = inps
                    .split_whitespace()
                    .map(|s| ["-", s].concat())
                    .collect::<Vec<String>>();
                let output = Command::new("java")
                    .arg("-jar")
                    .args(inps)
                    .arg(file)
                    .spawn()?
                    .wait_with_output()?;

                if !output.status.success() {
                    return Err(anyhow!("jar command failed to start"));
                }
            }
            Action::Edit { file } => {
                let output = Command::new("vi")
                   .arg(file)
                   .spawn()?
                   .wait_with_output()?;
                if!output.status.success() {
                    return Err(anyhow!("vi command failed to start"));
                }
            }
        _ => {println!("rtcmd")}
        }
        Ok(())
    }
}
